package com.king.modules.system.service.impl;

import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.king.common.constant.Constants;
import com.king.common.exception.AssertHelper;
import com.king.common.utils.DateTimeUtil;
import com.king.common.utils.file.ImageUtils;
import com.king.common.utils.text.StringUtils;
import com.king.framework.config.ProjectConfig;
import com.king.modules.system.domain.AttachmentEntity;
import com.king.modules.system.domain.vo.file.FileType;
import com.king.modules.system.domain.vo.file.FileUploadDto;
import com.king.modules.system.domain.vo.file.FileUploadVo;
import com.king.modules.system.domain.vo.file.ImgUploadDto;
import com.king.modules.system.domain.vo.file.MultiFilesUploadDto;
import com.king.modules.system.domain.vo.file.MultiImgsUploadDto;
import com.king.modules.system.service.AttachmentService;

/**
 * 
 * @author lenovo
 *
 */
@Component
public class FileService {
	
    public static final String STR_RANDOM = "0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLMNBVCXZ";

    @Autowired
    private AttachmentService attachmentService;


    /**
     * 文件上传
     *
     * @param dto
     * @return
     * @throws IOException
     */
    @Transactional
    public FileUploadVo uploadImg(ImgUploadDto dto) throws Exception {
        //附件
        MultipartFile multipartFile = dto.getFile();
        //原文件名
        String originalFilename = multipartFile.getOriginalFilename();
        //获取文件的后缀
        String suffix = StringUtils.substringAfterLast(originalFilename, ".");
        AssertHelper.getInstance().checkException(!this.isImage(suffix), "图片类型不支持");
        // 保存文件
        String relativePath = this.saveFile(multipartFile, FileType.img.name(), dto.getModuleName(), dto.getEntityId(), null);
        //原文件保存的绝对路径
        String absPath = this.buildAbsPath(relativePath);
        //原文件
        File originalFile = new File(absPath);
        //压缩图片
        ImageUtils.reduceImage(originalFile);
        //压缩图片相对路径
        String reduceImage = ImageUtils.getReduceImgPath(relativePath);
        // 新增附件记录
        AttachmentEntity attachmentEntity = this.addAttachement(dto.getModuleName(), dto.getEntityId(), FileType.img.name(), originalFilename, relativePath, multipartFile.getSize());
        return FileUploadVo.newInstance().setFileName(attachmentEntity.getSavename()).setUri(reduceImage).setOriginalImgUri(relativePath);
    }

    /**
     * 新增附件记录
     *
     * @param moduleName   模块名
     * @param entityId     实体id
     * @param fileType     文件类型
     * @param originalName 原文件名
     * @param relativePath 文件相对路径
     * @param fileSize     文件大小
     * @throws Exception
     */
    @Transactional
    public AttachmentEntity addAttachement(String moduleName, String entityId, String fileType, String originalName, String relativePath, long fileSize) throws Exception {
        //存储的文件名
        String saveFileName = StringUtils.substringAfterLast(relativePath, "/");
        //文件后缀
        String suffix = StringUtils.substringAfterLast(relativePath, ".").toUpperCase();
        //压缩图片路径
        String reduceImagePath = null;
        if (FileType.img.name().equalsIgnoreCase(fileType)) {
            reduceImagePath = ImageUtils.getReduceImgPath(relativePath);
        }
        AttachmentEntity attachmentEntity = AttachmentEntity.newInstance();
        attachmentEntity.setBtype(moduleName);
        attachmentEntity.setBid(entityId);
        attachmentEntity.setBclass(fileType);
        attachmentEntity.setFilepath(relativePath);
        attachmentEntity.setCompresspath(reduceImagePath);
        attachmentEntity.setFiletype(suffix);
        attachmentEntity.setSavename(saveFileName);
        attachmentEntity.setFilename(originalName);
        attachmentEntity.setFilesize(String.valueOf(fileSize));
        attachmentEntity.setUptime(new Date());
        this.attachmentService.doAdd(attachmentEntity);
        return attachmentEntity;
    }

    /**
     * 上传多张图片
     *
     * @param dto
     * @return
     */
    @Transactional
    public List<FileUploadVo> uploadMultiImg(MultiImgsUploadDto dto) throws Exception {
        List<FileUploadVo> uploadVos = new ArrayList<>();
        for (MultipartFile file : dto.getFiles()) {
            ImgUploadDto imgUploadDto = ImgUploadDto.newInstance().setFile(file).setEntityId(dto.getEntityId()).setModuleName(dto.getModuleName());
            //图片保存的相对路径
            FileUploadVo fileUploadVo = this.uploadImg(imgUploadDto);
            uploadVos.add(fileUploadVo);
        }
        return uploadVos;
    }

    /***
     * true表示是图片文件
     * @param suffix
     * @return
     */
    public boolean isImage(String suffix) {
    	 List<String> imageSuffixs = Constants.imageSuffixs;
        for (String type : imageSuffixs) {
            if (type.equalsIgnoreCase(suffix))
                return true;
        }
        return false;
    }

    /**
     * 删除文件
     *
     * @param filePath
     * @return
     */
    @Transactional
    public boolean delete(String filePath) {
        String absPath = this.buildAbsPath(filePath);
        File file = new File(absPath);
        if (file.exists()) {
            return file.delete();
        }
        return false;
    }

    /**
     * 文件上传
     *
     * @param dto
     * @return
     * @throws IOException
     */
    @Transactional
    public FileUploadVo uploadFile(FileUploadDto dto) throws Exception {
        AssertHelper.getInstance().checkException(StringUtils.isBlank(dto.getFileType()), "请选择文件类型");
        if (FileType.img.name().equalsIgnoreCase(dto.getFileType())) {
            //上传图片类型
            return this.uploadImg(ImgUploadDto.of(dto));
        }
        // 保存文件
        MultipartFile file = dto.getFile();
        String relativePath = this.saveFile(file, dto.getFileType(), dto.getModuleName(), dto.getEntityId(), dto.getSaveFileName());
        // 新增附件记录
        AttachmentEntity attachmentEntity = this.addAttachement(dto.getModuleName(), dto.getEntityId(), dto.getFileType(), file.getOriginalFilename(), relativePath, file.getSize());
        return FileUploadVo.newInstance().setFileName(attachmentEntity.getSavename()).setUri(relativePath);
    }

    /**
     * 多文件上传
     *
     * @param dto
     * @return
     * @throws IOException
     */
    @Transactional
    public List<FileUploadVo> uploadMultiFiles(MultiFilesUploadDto dto) throws Exception {
        List<FileUploadVo> fileUploadVos = new ArrayList<>();
        for (MultipartFile file : dto.getFiles()) {
            FileUploadDto fileUploadDto = FileUploadDto.newInstance()
                    .setFileType(dto.getFileType())
                    .setFile(file)
                    .setEntityId(dto.getEntityId())
                    .setModuleName(dto.getModuleName());
            //文件保存的相对路径
            FileUploadVo fileUploadVo = this.uploadFile(fileUploadDto);
            fileUploadVos.add(fileUploadVo);
        }
        return fileUploadVos;
    }

    /**
     * 保存文件
     *
     * @param multipartFile 附件
     * @param fileType      文件类型
     * @param moduleName    模块名
     * @param entityId      实体id
     * @param saveFileName  保存的文件名
     * @return
     */
    private String saveFile(MultipartFile multipartFile, String fileType, String moduleName, String entityId, String saveFileName) throws IOException {
        // 如果用户没有主动设置保存的文件名，则通过原始文件名生成保存的文件名
        if (StringUtils.isBlank(saveFileName))
            saveFileName = this.buildSaveFileName(multipartFile.getOriginalFilename());
        //构建文件的相对路径
        String relativePath = this.buildRelativePath(fileType, moduleName, entityId, saveFileName);
        //文件保存的绝对路径
        String absPath = this.buildAbsPath(relativePath);
        File file = new File(absPath);
        multipartFile.transferTo(file);
        return relativePath;
    }

    /**
     * 获取绝对路径
     *
     * @param relativePath 文件uri
     * @return
     */
    public String buildAbsPath(String relativePath) {
        final String filePattern = "{}/{}";
        return StringUtils.format(filePattern, this.getUploadDir(), relativePath);
    }

    /**
     * 构建相对路径
     *
     * @param fileType
     * @param moduleName
     * @param entityId
     * @param saveFileName
     * @return
     */
    private String buildRelativePath(String fileType, String moduleName, String entityId, String saveFileName) {
        // 获取当前日期字符串
        String dateStr = this.getDateStr();
        // 创建文件夹
        this.createFileDirIfNotExist(fileType, moduleName, entityId, dateStr);
        // /{fileType}/{moduleName}/{entityId}/{dateStr}/{saveFileName}
        final String pattern = "{}/{}/{}/{}/{}";
        return StringUtils.format(pattern, fileType, moduleName, entityId, dateStr, saveFileName);
    }

    /**
     * 如果用户没有主动设置保存的文件名，则通过原始文件名生成保存的文件名
     *
     * @param originalName 原始文件名
     * @return
     */
    private String buildSaveFileName(String originalName) {
        //获取文件的后缀
        String suffix = StringUtils.substringAfterLast(originalName, ".");
        //获取当前日期时间字符串
        String dateTimeStr = this.getDateTimeStr();
        String uuid = RandomStringUtils.random(4, STR_RANDOM);
        return new StringBuilder(dateTimeStr).append("_").append(uuid).append(".").append(suffix).toString();
    }

    /**
     * 获取当前日期字符串
     *
     * @return
     */
    private String getDateStr() {
        return DateTimeUtil.timeStamp2DataStr(Instant.now().toEpochMilli());
    }

    /**
     * 获取当前日期时间字符串
     *
     * @return
     */
    private String getDateTimeStr() {
        return DateTimeUtil.timeStamp2DataTimeStr(Instant.now().toEpochMilli(), "yyMMddHHmmssSSS");
    }

    /**
     * 创建文件夹
     *
     * @param fileType
     * @param moduleName
     * @param entityId
     * @param dateStr
     */
    private void createFileDirIfNotExist(String fileType, String moduleName, String entityId, String dateStr) {
        final String pattern = "{}/{}/{}/{}/{}";
        String fileDir = StringUtils.format(pattern, this.getUploadDir(), fileType, moduleName, entityId, dateStr);
        File dir = new File(fileDir);
        if (!dir.exists()) {
            if (!dir.mkdirs()) throw new RuntimeException("目录创建失败");
        }
    }

    /**
     * 文件上传目录
     *
     * @return
     */
    private String getUploadDir() {
        return ProjectConfig.getProfile();
    }

}
